/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2024年6月19日
 * V4.0
 */
package com.jphenix.share.tools;
//#region 【引用区】
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import com.jphenix.standard.docs.ClassInfo;
//#endregion

//#region 【类说明】
/**
 * 文件读取写入缓存工具类
 * com.jphenix.share.tools.FileBuffer
 * 
 * 特点：
 *       1. 只管读，读多读少不用介意。实际上并不是每次都从文件中读取数据。只是先从文件中读取到缓存里，一次一次输出，直到缓存中都输出完后，再从问价中读取。
 *       2. 只管写，不用介意缓存是否满了。实际上并不是每次调用都写入文件，只是放在缓存中，直到缓存已满自动写入文件。
 *       3. 在都操作完以后，记得执行关闭全部close或者关闭输出流closeOutput，这样会自动将缓存中剩余内容写入文件。或者主动调用flush将缓存中剩余内容写入文件。
 *       4. 如果直接复制文件，不妨直接调用copy方法即可，效率最高
 *       5. 如果读取出来的内容需要加工，能字节处理，就不要字符串处理，否则效率就降低了。作为缓存的字节数组大小要低于FileBuffer预设的缓存大小，这样可以避免
 *          FileBuffer频繁读取文件和频繁写入文件。每次读取和写入都会花费一定时间。如果仅仅是在读取行数据后，在这一行数据中增加固定长度的内容，建议字节数组
 *          长度+这行增加的内容长度=FileBuffer的缓存大小
 *         
 * 2024-07-25 完善了注释说明
 *          
 * @author MBG
 * 2024年6月19日
 */
//#endregion
@ClassInfo({"2024-07-25 15:24","文件读取写入缓存工具类"})
public class FileBuffer {

	//#region 【变量声明区】
	private String           inputFilePath    = null;  // 读取文件的全路径
	private String           outputFilePath   = null;  // 写入文件全路径（包括文件名）
	private FileInputStream  fis              = null;  // 文件读入流
	private FileOutputStream fos              = null;  // 文件写入流
	private FileChannel      inChannel        = null;  // 文件读取通道
	private FileChannel      outChannel       = null;  // 文件写入通道
	private ByteBuffer       readBuffer       = null;  // 读取缓存
	private ByteBuffer       writeBuffer      = null;  // 写入缓存
	private boolean          enabledRead      = false; // 是否允许读取
	private boolean          enabledWrite     = false; // 是否允许写入
	private boolean          firstRead        = false; // 是否首次读取完毕
	private String           encoding         = null;  // 读取和写入字符串的编码格式
	//#endregion
	
	//#region 【构造函数】
	/**
	 * 构造函数
	 * @author MBG
	 */
	public FileBuffer(int bufferSize) {
		super();
		this.readBuffer  = ByteBuffer.allocate(bufferSize);
		this.writeBuffer = ByteBuffer.allocate(bufferSize);
	}
	//#endregion

	//#region setInputFilePath(path) 设置读取文件的全路径
	/**
	 * 设置读取文件的全路径
	 * @param path       读取文件的全路径
	 * @return           当前类实例
	 * @throws Exception 异常
	 * 2024年6月19日
	 * @author MBG
	 */
	public FileBuffer setInputFilePath(String path) throws Exception {
		closeInput(); // 先关闭上一个读取文件相关对象
		this.inputFilePath = path;
		this.fis           = new FileInputStream(path);
		this.inChannel     = fis.getChannel();
		this.enabledRead   = true;
		return this;
	}
	//#endregion
	
	//#region readLine(bytes) 读取数据到bytes字节数组中，直到遇到\r\n或者\n结束本次读取
	/**
	 * 读取数据到bytes字节数组中，直到遇到\r\n或者\n结束本次读取
	 * @param bytes        需要设置值的字节数组
	 * @return             读取的字节长度
	 * 
	 * -1              读取完毕
	 * <= bytes.length 读取完一整行数据
	 * >  bytes.length bytes缓存已经装满，但还没读完这一整行的数据
	 * 
	 * 注意：此处返回的读取字节长度值，如果超过bytes缓存数组的长度，这一行的数据的长度，超出了缓存长度
	 * 
	 * @throws Exception   异常
	 * 2024年6月19日
	 * @author MBG
	 */
	public int readLine(byte[] bytes) throws Exception {
		if(!enabledRead || bytes==null || bytes.length<1) {
			return -1;
		}
		int matchIndex = 0; // matchBytes 的数组索引
		int readedSize = 0; // 已经读取了字节数量
		int[] matchRes;     // 处理匹配信息的返回值
		
		//#region 缓存中还剩些内容
		if(firstRead && readBuffer.limit()>readBuffer.position()) {
			
			matchRes = matchLine(bytes,0,matchIndex);
			
			//#region 已经匹配到
			if(matchRes[1]==2) {
				return matchRes[0];
			}
			//#endregion
			
			readedSize = matchRes[0]; // 放入已经设置到bytes中的字节长度
			matchIndex = matchRes[1]; // 放入已经匹配的matchBytes索引
			
			//#region 没匹配到，但已经达到 bytes 上限
			if(bytes.length<=readedSize) {
				// 达到bytes上线，并且没匹配到时，返回的值比bytes.length大
				return readedSize+1;
			}
			//#endregion
		}
		//#endregion
		
		// 走到这里，说明缓存中的数据并没有完全匹配matchBytes值
		
		//#region 循环读取数据，直到匹配到\r\n 或者\n
		while(true) {
			
			//#region 读取文件
			readBuffer.clear();
			int doReadSize = inChannel.read(readBuffer);
			readBuffer.flip();
			firstRead = true;
			if(doReadSize<1) {
				// -1 1 2 3 ..  没有0
				return readedSize<1?-1:readedSize;
			}
			//#endregion
			
			matchRes = matchLine(bytes,readedSize,matchIndex);
			
			//#region 已经匹配到
			if(matchRes[1]==2) {
				return matchRes[0];
			}
			//#endregion
			
			readedSize = matchRes[0]; // 放入已经设置到bytes中的字节长度
			matchIndex = matchRes[1]; // 放入已经匹配的matchBytes索引
			
			//#region 没匹配到，但已经达到 bytes 上限
			if(bytes.length<=readedSize) {
				// 达到bytes上线，并且没匹配到时，返回的值比bytes.length大
				return readedSize+1;
			}
			//#endregion
		}
		//#endregion
	}
	//#endregion
	
	//#region read(bytes,matchBytes) 读取数据到bytes字节数组中，直到读取的数据匹配matchBytes字节数组位置（返回数据中不包含匹配的matchBytes数据）
	/**
	 * 读取数据到bytes字节数组中，直到读取的数据匹配matchBytes字节数组位置（返回数据中不包含匹配的matchBytes数据）
	 * @param bytes       读取数据存放的字节数组
	 * @param matchBytes  匹配的字节数组
	 * @return            读取字节长度
	 * 2024年6月19日
	 * @author MBG
	 */
	public int read(byte[] bytes,byte[] matchBytes) throws Exception {
		
		//#region 变量声明区
		if(!enabledRead || bytes==null || bytes.length<1 || matchBytes==null || matchBytes.length<1) {
			return -1;
		}
		int matchIndex = 0; // matchBytes 的数组索引
		int readedSize = 0; // 已经读取了字节数量
		int[] matchRes;     // 处理匹配信息的返回值
		//#endregion
		
		//#region 缓存中还剩些内容
		if(firstRead && readBuffer.limit()>readBuffer.position()) {
			
			matchRes = matchBytes(bytes,0,matchBytes,matchIndex);
			
			//#region 已经匹配到
			if(matchRes[1]==matchBytes.length) {
				return matchRes[0];
			}
			//#endregion
			
			readedSize = matchRes[0]; // 放入已经设置到bytes中的字节长度
			matchIndex = matchRes[1]; // 放入已经匹配的matchBytes索引
			
			//#region 没匹配到，但已经达到 bytes 上限
			if(bytes.length<=readedSize) {
				// 达到bytes上线，并且没匹配到时，返回的值比bytes.length大
				return readedSize+1;
			}
			//#endregion
		}
		//#endregion
		
		// 走到这里，说明缓存中的数据并没有完全匹配matchBytes值
		
		while(true) {
			
			//#region 读取文件
			readBuffer.clear();
			int doReadSize = inChannel.read(readBuffer);
			readBuffer.flip();
			firstRead = true;
			if(doReadSize<1) {
				// -1 1 2 3 ..  没有0
				return readedSize<1?-1:readedSize;
			}
			//#endregion
			
			matchRes = matchBytes(bytes,readedSize,matchBytes,matchIndex);
			
			//#region 已经匹配到
			if(matchRes[1]==matchBytes.length) {
				return matchRes[0];
			}
			//#endregion
			
			readedSize = matchRes[0]; // 放入已经设置到bytes中的字节长度
			matchIndex = matchRes[1]; // 放入已经匹配的matchBytes索引
			
			//#region 没匹配到，但已经达到 bytes 上限
			if(bytes.length<=readedSize) {
				// 达到bytes上线，并且没匹配到时，返回的值比bytes.length大
				return readedSize+1;
			}
			//#endregion
		}
	}
	//#endregion
	
	//#region read(bytes,readSize) 读取指定长度的数据 
	/**
	 * 读取指定长度的数据
	 * 
	 * 特点：如果获取字节长度小于文件缓存长度，则先不从文件中读取，先从
	 * 文件缓存中把数据读完后，再从文件中读取新的数据
	 * 
	 *       如果获取字节长度大于文件缓存长度，则自动多次读取文件，设置到
	 *       返回的字节数组中
	 * 
	 * 
	 * @param bytes       读取后存放在这个字节数组中
	 * @param readSize    读取长度
	 * @return            实际读取长度
	 * @throws Exception  异常
	 * 2024年6月19日
	 * @author MBG
	 */
	public int read(byte[] bytes,int readSize) throws Exception {

		int readedSize = 0; // 已经读取了字节数量
		
		//#region 缓存中还剩些内容
		if(firstRead && readBuffer.limit()>readBuffer.position()) {
			
			//#region 缓存中剩余的内容足够返回用
			if(readSize<=readBuffer.limit()-readBuffer.position()) {
				readBuffer.get(bytes,0,readSize);
				return readSize;
			}
			//#endregion
			
			//#region 先将缓存中剩余的数据放入返回数组中
			readedSize = readBuffer.limit()-readBuffer.position();
			readBuffer.get(bytes,0,readedSize);
			readSize -= readedSize; // 计算剩余读取数量
			//#endregion
			
		}
		//#endregion
		
		while(readSize>0) {
			
			//#region 读取文件
			readBuffer.clear();
			int doReadSize = inChannel.read(readBuffer);
			readBuffer.flip();
			firstRead = true;
			if(doReadSize<1) {
				// -1 1 2 3 ..  没有0
				return readedSize<1?-1:readedSize;
			}
			//#endregion
			
			//#region 重新读取后，数据足够返回
			if(readSize<=readBuffer.limit()-readBuffer.position()) {
				readBuffer.get(bytes,readedSize,readSize);
				return readedSize+readSize; // 已经读取数量+本次读取数量
			}
			//#endregion
			
			//#region 本次读取的内容也不够返回数据
			readBuffer.get(bytes,readedSize,readedSize+doReadSize);
			readedSize+=doReadSize;
			readSize-=doReadSize;
			//#endregion
			
		}
		
		return readedSize;
	}
	//#endregion
	
	//#region write(... chars) 写入多个字符
	/**
	 * 写入多个字符
	 * @param chars       多个字符
	 * @return            当前类实例
	 * @throws Exception  异常
	 * 2024年6月19日
	 * @author MBG
	 */
	public FileBuffer write(char...chars) throws Exception {
		if(chars==null) {
			return this;
		}
		byte[] bytes;
		for(char ele:chars) {
			bytes = new byte[2];
			bytes[0] = (byte) ((ele & 0xFF00) >> 8);
			bytes[1] = (byte) (byte) (ele & 0xFF);
			write(bytes);
		}
		return this;
	}
	//#endregion
	
	//#region write(data) 写入一个字节
	/**
	 * 写入一个字节
	 * @param data       一个字节数据
	 * @return           当前类实例
	 * @throws Exception 异常
	 * 2024年6月19日
	 * @author MBG
	 */
	public FileBuffer write(byte data) throws Exception {
		if(!enabledWrite) {
			return this;
		}
		//#region 缓存已满，先写入一批数据
		if(writeBuffer.limit()>writeBuffer.position()+1) {
			writeBuffer.flip();
			outChannel.write(writeBuffer);
			writeBuffer.clear();
		}
		//#endregion
		
		// 将该字节放入缓存
		writeBuffer.put(data);
		
		return this;
	}
	//#endregion
	
	//#region writeLineLf(str) 写入行（末尾自动带上Lf控制字符 Linux Unix格式）
	/**
	 * 写入行（末尾自动带上Lf控制字符 Linux Unix格式）
	 * @param str         写入内容（不带换行符）
	 * @return            当前类实例
	 * @throws Exception  异常
	 * 2024年6月19日
	 * @author MBG
	 */
	public FileBuffer writeLineLf(String str) throws Exception {
		write(str);
		write((byte)'\n');
		return this;
	}
	//#endregion
	
	//#region writeLineCrLf(str) 写入行（末尾自动带上 CrLf控制字符 Windows格式）
	/**
	 * 写入行（末尾自动带上 CrLf控制字符 Windows格式）
	 * @param str         写入内容（不带回车换行符）
	 * @return            当前类实例
	 * @throws Exception  异常
	 * 2024年6月19日
	 * @author MBG
	 */
	public FileBuffer writeLineCrLf(String str) throws Exception {
		write(str);
		write((byte)'\r');
		write((byte)'\n');
		return this;
	}
	//#endregion
	
	//#region writeLineLf(... datas) 写入行数据，并自动在末尾加上换行符（\n 即Linux Unix格式）
	/**
	 * 写入行数据，并自动在末尾加上换行符（\n 即Linux Unix格式）
	 * @param datas       行数据字节数组（不包含回车换行符）
	 * @return            当前类实例
	 * @throws Exception  异常
	 */
	public FileBuffer writeLineLf(byte... datas) throws Exception {
		write(datas);
		write((byte)'\n');
		return this;
	}
	//#endregion
	
	//#region writeLineCrLf(datas) 写入行数据，并自动在末尾加上回车换行符（\r\n 即windows格式）
	/**
	 * 写入行数据，并自动在末尾加上回车换行符（\r\n 即windows格式）
	 * @param datas       行数据字节数组（不包含回车换行符）
	 * @return            当前类实例
	 * @throws Exception  异常
	 */
	public FileBuffer writeLineCrLf(byte... datas) throws Exception {
		write(datas);
		write((byte)'\r');
		write((byte)'\n');
		return this;
	}
	//#endregion
	
	//#region writeLineLf(datas,position,length) 写入行数据，并自动在末尾加上换行符（\n 即Linux Unix格式）
	/**
	 * 写入行数据，并自动在末尾加上换行符（\n 即Linux Unix格式）
	 * @param datas       行数据字节数组（不包含回车换行符）
	 * @return            当前类实例
	 * @throws Exception  异常
	 */
	public FileBuffer writeLineLf(byte[] datas,int position,int length) throws Exception {
		write(datas,position,length);
		write((byte)'\n');
		return this;
	}
	//#endregion
	
	//#region writeLineCrLf(datas,position,length) 写入行数据，并自动在末尾加上回车换行符（\r\n 即windows格式）
	/**
	 * 写入行数据，并自动在末尾加上回车换行符（\r\n 即windows格式）
	 * @param datas       行数据字节数组（不包含回车换行符）
	 * @return            当前类实例
	 * @throws Exception  异常
	 */
	public FileBuffer writeLineCrLf(byte[] datas,int position,int length) throws Exception {
		write(datas,position,length);
		write((byte)'\r');
		write((byte)'\n');
		return this;
	}
	//#endregion
	
	//#region write(str) 写入字符串
	/**
	 * 写入字符串
	 * @param str         字符串
	 * @return            当前类实例
	 * @throws Exception  异常
	 * 2024年6月19日
	 * @author MBG
	 */
	public FileBuffer write(String str) throws Exception {
		if(encoding!=null && encoding.length()>0) {
			write(str.getBytes(encoding));
		}else {
			write(str.getBytes());
		}
		return this;
	}
	//#endregion
	
	//#region write(bytes) 写入全部字节数组
	/**
	 * 写入全部字节数组
	 * @param bytes   字节数组
	 * @return        当前类实例
	 * 2024年6月19日
	 * @author MBG
	 */
	public FileBuffer write(byte...bytes) throws Exception {
		if(bytes==null) {
			return this;
		}
		write(bytes,0,bytes.length);
		return this;
	}
	//#endregion
	
	//#region write(bytes,position) 写入字节数组数据（从数组起始位置到数组末尾）
	/**
	 * 写入字节数组数据（从数组起始位置到数组末尾）
	 * @param bytes        字节数组
	 * @param position     起始索引位置
	 * @return             当前类实例
	 * @throws Exception   异常
	 * 2024年6月19日
	 * @author MBG
	 */
	public FileBuffer write(byte[] bytes,int position) throws Exception {
		if(bytes==null || bytes.length<1 || bytes.length<position) {
			return this;
		}
		write(bytes,position,bytes.length);
		return this;
	}
	//#endregion
	
	//#region write(bytes,position,size)  写入字节数组数据
	/**
	 * 写入字节数组数据
	 * @param bytes      字节数组
	 * @param position   起始索引位置
	 * @param size       数据长度
	 * @return           当前类实例
	 * @throws Exception 异常
	 * 2024年6月19日
	 * @author MBG
	 */
	public FileBuffer write(byte[] bytes,int position,int size) throws Exception {
		
		//#region 变量声明区
		if(!enabledWrite || bytes==null || bytes.length<=position || size<1) {
			return this;
		}
		if(position<0) {
			position = 0;
		}
		if(bytes.length<position+size) {
			size = bytes.length-position;
		}
		int lessSize    = size; // 剩余长度
		int bufferSize;
		//#endregion
		
		//#region 循环写入数据
		while(lessSize>0) {
			// 缓存剩余容量
			bufferSize = writeBuffer.limit()-writeBuffer.position();
			
			//#region 缓存已满，写入一批数据
			if(bufferSize<1) {
				writeBuffer.flip();
				outChannel.write(writeBuffer);
				writeBuffer.clear();
				bufferSize = writeBuffer.limit();
			}
			//#endregion
			
			//#region 需要写入的数据长度超出缓存剩余长度处理
			if(bufferSize<lessSize) {
				// 写入部分数据
				writeBuffer.put(bytes,position,bufferSize);
				position += bufferSize;
				lessSize -= bufferSize;
			}
			//#endregion
			
			//#region 需要写入的数据小于缓存剩余长度，先放入缓存不写文件
			else {
				// 写入剩余部分
				writeBuffer.put(bytes,position,lessSize);
				position += lessSize;
				lessSize = 0;
				break;
			}
			//#endregion
			
		}
		//#endregion
		
		return this;
	}
	//#endregion
	
	//#region 【内部方法】 matchLine(bytes,startPos,matchIndex) 从缓存中获取数据，直到匹配\r\n 或者 \n
	/**
	 * 从缓存中获取数据，直到匹配\r\n 或者 \n
	 * @param bytes       设置值的字节数组
	 * @param startPos    指向当前设置值的位置
	 * @param matchIndex  当前匹配的字节元素索引 值1:匹配到\r
	 * @return            数组 0:返回字节数组值长度  1:匹配索引 值0（没匹配到） 1（匹配到\r，然后缓存中没数据了） 2（匹配成功）
	 * 2024年6月19日
	 * @author MBG
	 */
	private int[] matchLine(byte[] bytes,int startPos,int matchIndex) {
		byte byteEle;
		while(readBuffer.limit()>readBuffer.position()) {
			byteEle = readBuffer.get();
			
			//#region 匹配到其中一个字节元素
			if(byteEle=='\n') {
				if(matchIndex>0) {
					// 之前匹配到了\r，指针往前挪一位，去掉之前放进去的\r
					startPos--;
				}
				return new int[] {startPos,2};
			}
			//#endregion
			
			//#region 没匹配到回车符
			else {
				if(byteEle=='\r') {
					// 匹配到换行符，做个标记
					matchIndex = 1;
				}else {
					matchIndex = 0;
				}
				bytes[startPos] = byteEle;
				startPos++;
				
				//#region 没匹配到，但是已经达到 bytes 上限
				if(bytes.length<=startPos) {
					return new int[] {bytes.length,matchIndex};
				}
				//#endregion
				
				continue;
			}
			//#endregion
		}
		return new int[] {startPos,matchIndex};
	}
	//#endregion
	
	//#region 【内部方法】 matchBytes(bytes,startPos,matchBytes,matchIndex) 从缓存中获取数据，直到匹配 matchBytes 字节数组终止读取
	/**
	 * 从缓存中获取数据，直到匹配 matchBytes 字节数组终止读取
	 * @param bytes       设置值的字节数组
	 * @param startPos    指向当前设置值的位置
	 * @param matchBytes  匹配字节数组
	 * @param matchIndex  当前匹配的字节元素索引
	 * @return            数组 0:返回字节数组值长度  1:匹配索引
	 * 2024年6月19日
	 * @author MBG
	 */
	private int[] matchBytes(byte[] bytes,int startPos,byte[] matchBytes,int matchIndex) {
		byte byteEle;
		while(readBuffer.limit()>readBuffer.position()) {
			byteEle = readBuffer.get();
			
			//#region 匹配到其中一个字节元素
			if(byteEle==matchBytes[matchIndex]) {
				matchIndex++; // 指向下一个索引元素
				if(matchBytes.length<=matchIndex) {
					// 完全匹配
					// 这时 startPos 值绝对大于等于 matchBytes 长度
					// 返回的字节数据值长度要减去已经设置匹配字节的长度 （当前匹配的字节并没设置到bytes中，所以要-1）
					// 返回的matchIndex已经等于matchBytes.length
					return new int[]{startPos-(matchBytes.length-1),matchIndex};
				} else {
					// 匹配其中一个元素
					bytes[startPos] = byteEle;
					startPos++;
					
					//#region 没匹配到，但是已经达到 bytes 上限
					if(bytes.length<=startPos) {
						return new int[] {bytes.length,matchIndex};
					}
					//#endregion
					
				}
			}
			//#endregion
			
			//#region 获取的字节并不匹配
			else {
				matchIndex = 0; // 复位匹配指针
				bytes[startPos] = byteEle;
				startPos++;
				
				//#region 没匹配到，但是已经达到 bytes 上限
				if(bytes.length<=startPos) {
					return new int[] {bytes.length,matchIndex};
				}
				//#endregion
			}
			//#endregion
		}
		return new int[] {startPos,matchIndex};
	}
	//#endregion
	
	//#region setOutputFilePath(path) 设置写入文件全路径（包括文件名）
	/**
	 * 设置写入文件全路径（包括文件名）
	 * @param path         写入文件全路径
	 * @return             当前类实例
	 * @throws Exception   异常
	 * 2024年6月19日
	 * @author MBG
	 */
	public FileBuffer setOutputFilePath(String path) throws Exception {
		
		//#region 判断目标文件所在路径是否存在，并自动创建路径和目标文件
		File checkFile = new File(path);
		File dir = checkFile.getParentFile();
		if(!dir.exists()) {
			Files.createDirectories(Paths.get(dir.getPath()));
		}
		checkFile.createNewFile();
		//#endregion
		
		this.outputFilePath = path;
		this.fos            = new FileOutputStream(path);
		this.outChannel     = fos.getChannel();
		this.enabledWrite   = true;
		
		return this;
	}
	//#endregion
	
	//#region closeInput() 关闭当前读取文件相关对象
	/**
	 * 关闭当前读取文件相关对象
	 * @return  当前类实例
	 * 2024年6月19日
	 * @author MBG
	 */
	public FileBuffer closeInput() {
		firstRead    = false;
		enabledRead = false;
		if(fis!=null) {
			try {
				fis.close();
			}catch(Exception e) {}
		}
		if(inChannel!=null) {
			try {
				inChannel.close();
			}catch(Exception e) {}
		}
		readBuffer.clear();
		return this;
	}
	//#endregion
	
	//#region closeOutput() 关闭写入文件相关对象
	/**
	 * 关闭写入文件相关对象
	 * @return  当前类实例
	 * 2024年6月19日
	 * @author MBG
	 */
	public FileBuffer closeOutput() {
		
		try {
			flush(); // 将缓存中剩余部分写入文件
		}catch(Exception e) {}
		
		enabledWrite = false;
		if(outChannel!=null) {
			try {
				outChannel.close();
			}catch(Exception e) {}
		}
		if(fos!=null) {
			try {
				fos.close();
			}catch(Exception e) {}
		}
		writeBuffer.clear();
		
		return this;
	}
	//#endregion

	//#region getInputFilePath() 获取读取文件的全路径
	/**
	 * 获取读取文件的全路径
	 * @return 读取文件的全路径
	 * 2024年6月19日
	 * @author MBG
	 */
	public String getInputFilePath() {
		return inputFilePath;
	}
	//#endregion
	
	//#region getOutputFilePath() 写入文件全路径
	/**
	 * 获取写入文件全路径
	 * @return 写入文件全路径
	 * 2024年6月19日
	 * @author MBG
	 */
	public String getOutputFilePath() {
		return outputFilePath;
	}
	//#endregion
	
	//#region flush() 将缓存中剩余部分写入缓存
	/**
	 * 将缓存中剩余部分写入缓存
	 * @return           当前类实例
	 * @throws Exception 异常
	 * 2024年6月19日
	 * @author MBG
	 */
	public FileBuffer flush() throws Exception {
		writeBuffer.flip();
		outChannel.write(writeBuffer);
		writeBuffer.clear();
		return this;
	}
	//#endregion
	
	//#region copy() 将读取文件内容全部写入目标文件
	/**
	 * 将读取文件内容全部写入目标文件
	 * @return           当前类实例
	 * @throws Exception 异常
	 * 2024年6月19日
	 * @author MBG
	 */
	public FileBuffer copy() throws Exception {
		flush();
        int length = inChannel.read(writeBuffer);
        while(length != -1){
        	writeBuffer.flip();
            outChannel.write(writeBuffer);
            writeBuffer.clear();
            // 再次读取文本内容
            length = inChannel.read(writeBuffer);
        }
        return this;
	}
	//#endregion
	
	//#region close() 全部关闭（读取文件和写入文件相关对象）
	/**
	 * 全部关闭（读取文件和写入文件相关对象）
	 * @return  当前类实例
	 * 2024年6月19日
	 * @author MBG
	 */
	public FileBuffer close() {
		closeInput();  // 关闭读取文件相关对象
		closeOutput(); // 关闭写入文件相关对象
		return this;
	}
	//#endregion
	
	//#region setEncoding(enc) 设置读取和写入字符串的编码格式
	/**
	 * 设置读取和写入字符串的编码格式
	 * @param enc 编码
	 * @return    当前类实例
	 * 2024年6月19日
	 * @author MBG
	 */
	public FileBuffer setEncoding(String enc) {
		this.encoding = enc;
		return this;
	}
	//#endregion
	
	//#region getEncoding() 获取读取和写入字符串的编码格式
	/**
	 * 获取读取和写入字符串的编码格式
	 * @return 读取和写入字符串的编码格式
	 * 2024年6月19日
	 * @author MBG
	 */
	public String getEncoding() {
		return encoding;
	}
	//#endregion
}









